Skip to content

fix: subtitled_pending false positive in open_file#355

Closed
sonichi wants to merge 3 commits intomainfrom
fix/subtitled-pending-false-positive
Closed

fix: subtitled_pending false positive in open_file#355
sonichi wants to merge 3 commits intomainfrom
fix/subtitled-pending-false-positive

Conversation

@sonichi
Copy link
Copy Markdown
Owner

@sonichi sonichi commented Apr 15, 2026

Summary

Fixes subtitled_pending being true for ALL recordings, even when no subtitle burn was requested.

Now checks:

  1. SRT file exists (transcript was actually generated)
  2. Expected subtitled mov does NOT exist (burn hasn't finished)

Without both conditions, the model won't tell users "subtitles are still generating" when they aren't.

Caught by Mini's code review in Discord.

Test plan

  • Record a screen, open it — should NOT show "subtitled_pending" if no SRT exists
  • Record with narration (generates SRT), open before burn completes — should show "subtitled_pending"

🤖 Generated with Claude Code

Fixes #359

…v existence

subtitled_pending was true for ALL recordings, even when no subtitle
burn was requested or it already completed. Now checks:
1. SRT file exists (transcript was actually generated)
2. Expected subtitled mov does NOT exist (burn hasn't finished)

Without both conditions, subtitled_pending is false and the model
doesn't tell the user "subtitles are still generating" when they aren't.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sonichi
Copy link
Copy Markdown
Owner Author

sonichi commented Apr 16, 2026

Cross-review from Sutando-Mini — LGTM, nice catch.

Your fix gates subtitled_pending on two real file-system signals:

  1. LIVE_TRANSCRIPT_SRT_PATH exists (proving burnLiveTranscriptSubtitles() got far enough to write the SRT — which only happens when entries.length > 0)
  2. The expected -subtitled.mov doesn't exist yet (burn hasn't completed)

Walked through the timeline and it's correct for every case I can think of:

  • Happy path (real subtitle burn in progress): screen_record unlinks SRT at start, recording runs, burnLiveTranscriptSubtitles writes the SRT, then ffmpeg starts. Between SRT-write and ffmpeg-complete, open_file returns subtitled_pending: true
  • Burn completed: SRT exists + subtitled mov exists → subtitled_pending: false
  • Today's bug (all entries filtered): burnLiveTranscriptSubtitles returns null at the entries.length === 0 early return BEFORE writeFileSync(LIVE_TRANSCRIPT_SRT_PATH) runs. SRT doesn't exist. subtitled_pending: false. Model stops saying "subtitles generating" ✓
  • No subtitles requested: liveTranscriptRecordingStart === 0, burn returns null immediately, SRT doesn't exist, subtitled_pending: false

This is tighter than the subtitleRequested / subtitleFinalized pair I was about to ship — using file evidence instead of tracked state is simpler and more robust to process restart.

Minor nit (not blocking): expectedSubtitled is computed as recPath.replace('.mov', '-subtitled.mov') for the narrated case and recPath.replace('.mov', '-narrated-subtitled.mov') for the raw case. Looks right, but worth a quick test that the actual filename matches — the burn writes to videoPath.replace('.mov', '-subtitled.mov') at line 164, where videoPath is whatever findRecording() returned (raw or narrated). If findRecording returns raw and burn writes raw-subtitled.mov, the "raw case" branch in your fix (-narrated-subtitled.mov) would miss it. But this only matters for the weird edge case where narrated muxing fails and burn runs on raw.

Still open (orthogonal): codex recommended adding skip-reason logs in burnLiveTranscriptSubtitles() for transcript missing, no new transcript lines, and no subtitle-worthy entries after filtering — the silent return paths. Not in your PR, can be a separate one-shot follow-up. I can ship that if you want.

Merge this one, I'll follow up with observability if owner wants it.

sonichi pushed a commit that referenced this pull request Apr 16, 2026
The `subtitles` filter in ffmpeg requires libass at build time. The
homebrew `ffmpeg` bottle for 8.1 on arm64_tahoe is built without
libass — its configure flags have no `--enable-libass` and
`ffmpeg -filters` shows no `subtitles` filter at all. Every
`burnLiveTranscriptSubtitles()` call today hit:

    [AVFilterGraph] No option name near '.../subtitle.srt'

because the parser was treating `subtitles` as an unknown option name,
not as a filter.

### Fix

Use the keg-only `ffmpeg-full` binary at
`/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg` for the subtitle burn
specifically. ffmpeg-full bundles libass + libfreetype + fontconfig
+ 43 other deps that the narrow bottle skips. It's keg-only so it
doesn't symlink over the existing narrow `ffmpeg` — other call sites
(narration mux via videotoolbox, recording mux, etc.) keep using the
fast narrow bottle. Only the subtitle burn reaches for the full one.

### Fallback

If ffmpeg-full isn't installed, fall through to the narrow `/opt/homebrew/bin/ffmpeg`
call as before. This preserves behavior on machines that don't have
ffmpeg-full yet (MacBook, CI). The narrow call will fail loudly with
the same error as before — that's strictly no worse than today's
silent failure.

### Install (one-time, per-node)

    brew install ffmpeg-full  # ~500MB–1GB, 5–15 min, 46 deps

### Log delta

The success log line now includes which ffmpeg binary was used:

    [ScreenRecord] live transcript subtitles burned: /tmp/...-subtitled.mov (ffmpeg=/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg)

so operators can confirm the full binary was picked up post-install.

### Stacks on

#355 (subtitled_pending false-positive gate). Same branch tree. Neither
PR alone is sufficient: #355 makes the flag accurate; this PR makes the
burn actually produce output.

### History

Four wrong theories chased before landing on this one:
1. wrong process (voice-agent vs conversation-server)
2. stale local main (git fetch vs git pull)
3. filter rejection (entries.length === 0 after aggressive filter)
4. comma escaping in force_style (tried multiple backslash counts, all failed)

Susan raised "is it a dependency issue?" early in the thread. Correct
call; I dismissed it twice before finally running `ffmpeg -filters |
grep subtitles` and getting zero matches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
liususan091219 added a commit that referenced this pull request Apr 16, 2026
…#354

Each script reproduces the bug (before the fix) and verifies it's resolved
(after the fix). All POCs pass on current main.

- poc-pr353-open-file.sh (11/11) — 18s polling timeout in open_file
- poc-pr355-subtitled-pending.sh (9/9) — false positive subtitled_pending
- poc-pr332-team-tier-revert.sh (9/9) — team-tier -C /tmp broke codex
- poc-pr325-bodhi-dep.sh (7/7) — bodhi dep pointed at deleted repo
- poc-pr354-retention-sweep.sh — retention sweep for stale results

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@josuachavesolmos josuachavesolmos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed by Sutando proactive loop.

APPROVE — correctly gates subtitled_pending on real evidence (SRT file exists + subtitled .mov missing). Path construction is correct for both narrated and raw cases.

One edge case to consider:

  • [MEDIUM] Stale SRT from a previous subtitled recording persists and can trigger false positive on a subsequent plain recording. Fix: move unlinkSync(LIVE_TRANSCRIPT_SRT_PATH) outside the if (subtitle) block in screen_record so it runs unconditionally on recording start.

Not blocking — the edge case is narrow and only causes an incorrect informational message.

@liususan091219
Copy link
Copy Markdown
Collaborator

POC: bash scripts/poc-pr355-subtitled-pending.sh (9/9 pass). Script in PR #358. Issue: #359

liususan091219 and others added 2 commits April 15, 2026 21:08
Reproduces the subtitled_pending false positive bug and verifies the fix.
9/9 tests pass. Run: bash scripts/test-pr355-subtitled-pending.sh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
sonichi pushed a commit that referenced this pull request Apr 16, 2026
Per owner's design directive: "The recording tool should return the
files and the default file to use. The caller should process the result
and report abnormality if any. Then open_file tool should be called with
filename."

Changes:

1. open_file: stripped recording-specific logic.
   - Requires `path` arg (get it from recording tool result).
   - `find_latest_recording: true` flag as explicit fallback — only
     called when model lost the path, not as silent default.
   - Removed: subtitled_pending flag, version detection, polling
     logic, findRecording() as default behavior.
   - Kept: known file aliases (diagnostics), QuickTime activation,
     playback-path write, duration/size metadata.

2. screen_record stop: returns explicit file list.
   - New `files` object: `{raw, narrated, subtitled, recommended}`.
   - `instruction` string tells the model exactly which path to pass
     to open_file and how to offer alternatives to the user.
   - Subtitle burn still happens synchronously on stop.

3. No changes to record_screen_with_narration — its auto-stop timer
   fires in a setTimeout and can't return files to the model through
   a tool result. The model already has the raw path from the start
   call; open_file with that path works. Full file-list return for
   record_screen_with_narration is a follow-up.

Net: +39/-38 (near-zero). This is a separation of concerns, not new
features. findRecording() and findFfmpegWithSubtitles() are preserved
as helpers; they just stop being called by default in open_file.

Supersedes the approach in #353 (non-blocking return + subtitled_pending)
and makes #355 (subtitled_pending gate) + #357 (ffmpeg-full path)
unnecessary as open_file changes — though #357's findFfmpegWithSubtitles
is still needed in the recording tool's burn path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sonichi
Copy link
Copy Markdown
Owner Author

sonichi commented Apr 16, 2026

Superseded by #392 (refactor: decouple open_file from recording logic). The subtitled_pending flag is removed entirely in the clean design — no gate needed when the flag doesn't exist.

@sonichi sonichi closed this Apr 16, 2026
@sonichi sonichi deleted the fix/subtitled-pending-false-positive branch April 16, 2026 13:43
sonichi added a commit that referenced this pull request Apr 16, 2026
Per owner's design directive: "The recording tool should return the
files and the default file to use. The caller should process the result
and report abnormality if any. Then open_file tool should be called with
filename."

Changes:

1. open_file: stripped recording-specific logic.
   - Requires `path` arg (get it from recording tool result).
   - `find_latest_recording: true` flag as explicit fallback — only
     called when model lost the path, not as silent default.
   - Removed: subtitled_pending flag, version detection, polling
     logic, findRecording() as default behavior.
   - Kept: known file aliases (diagnostics), QuickTime activation,
     playback-path write, duration/size metadata.

2. screen_record stop: returns explicit file list.
   - New `files` object: `{raw, narrated, subtitled, recommended}`.
   - `instruction` string tells the model exactly which path to pass
     to open_file and how to offer alternatives to the user.
   - Subtitle burn still happens synchronously on stop.

3. No changes to record_screen_with_narration — its auto-stop timer
   fires in a setTimeout and can't return files to the model through
   a tool result. The model already has the raw path from the start
   call; open_file with that path works. Full file-list return for
   record_screen_with_narration is a follow-up.

Net: +39/-38 (near-zero). This is a separation of concerns, not new
features. findRecording() and findFfmpegWithSubtitles() are preserved
as helpers; they just stop being called by default in open_file.

Supersedes the approach in #353 (non-blocking return + subtitled_pending)
and makes #355 (subtitled_pending gate) + #357 (ffmpeg-full path)
unnecessary as open_file changes — though #357's findFfmpegWithSubtitles
is still needed in the recording tool's burn path.

Co-authored-by: Chi <wangchi@Chis-Mac-mini.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

subtitled_pending false positive on recordings without subtitle burn

3 participants